﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XSot.BitBrowser.LocalApiClient.Model;

namespace XSot.BitBrowser.LocalApiClient
{
    public partial class BitBrowserLocalApiClient : IBitBrowserLocalApiClient
    {
        /// <summary>
        /// 健康检查接口，无参数，可以用来测试 Local Server 是否连接成功
        /// </summary>
        /// <returns></returns>
        public Task<HttpResult<string>> HealthAsync()
        {
            return GetResponseAsync<string>("/health", new());
        }
        /// <summary>
        /// 创建浏览器窗口，指定id则更新，否则创建
        /// </summary>
        /// <param name="profile"></param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserProfile>> CreateOrUpdateBroswerAsync(BitBrowserProfile profile)
        {
            profile ??= new();
            profile.FingerPrint ??= new();

            return GetResponseAsync<BitBrowserProfile>("/browser/update", profile);
        }
        /// <summary>
        /// 打开浏览器窗口
        /// </summary>
        /// <param name="id">浏览器窗口 id，创建完会返回 ID，或者通过 list 接口查询，或者界面上配置里点击【复制 ID】按钮复制，注意，ID 不是序号</param>
        /// <param name="queue">是否以队列方式打开，设置为 true 后，可有效防止多线程同时启动时导致的并发报错</param>
        /// <param name="args">浏览器启动参数，合法的 chromium 启动参数均支持，数组类型</param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserOpenResult>> OpenBrowserAsync(string id, bool queue = true, params string[] args)
        {
            return GetResponseAsync<BitBrowserOpenResult>("/browser/open", new { id, queue, args });
        }
        /// <summary>
        /// 关闭浏览器窗口
        /// </summary>
        /// <param name="id">浏览器窗口 id，创建完会返回 ID，或者通过 list 接口查询，或者界面上配置里点击【复制 ID】按钮复制，注意，ID 不是序号</param>
        /// <returns></returns>
        public Task<HttpResult<string>> CloseBrowserAsync(string id)
        {
            return GetResponseAsync<string>("/browser/close", new { id });
        }
        /// <summary>
        /// 重置浏览器关闭状态
        /// </summary>
        /// <param name="id">浏览器窗口 id</param>
        /// <returns></returns>
        public Task<HttpResult<string>> ResetBroswerClosingAsync(string id)
        {
            return GetResponseAsync<string>("/browser/closing/reset", new { id });
        }
        /// <summary>
        /// 删除浏览器窗口
        /// </summary>
        /// <param name="id">浏览器窗口 id</param>
        /// <returns></returns>
        public Task<HttpResult<string>> DeleteBrowserAsync(string id)
        {
            return GetResponseAsync<string>("/browser/delete", new { id });
        }

        /// <summary>
        /// 获取浏览器窗口详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserProfile>> GetBrowserAsync(string id)
        {
            return GetResponseAsync<BitBrowserProfile>("/browser/detail", new { id });
        }
        /// <summary>
        /// 分页获取浏览器窗口列表，page 参数从 0 开始，0 是第一页的数据
        /// </summary>
        /// <param name="page">分页，从 0 开始</param>
        /// <param name="pageSize">分页数量，最大 100，超出 100 仍然返回 100 条，默认 10</param>
        /// <param name="groupId">分组 ID，传入时查询此分组下的窗口列表</param>
        /// <param name="name">窗口名称，模糊匹配</param>
        /// <param name="seq">序号，精确查询</param>
        /// <param name="minSeq">最小序号，范围查询，不可与 seq 同时使用</param>
        /// <param name="maxSeq">最大序号，范围查询，不可与 seq 同时使用</param>
        /// <param name="sort">排序参数，只允许两个值，desc 倒序，asc 正序</param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserPageList<BitBrowserProfile>>> GetBrowserListAsync(int? page = 0, int? pageSize = 10, string? groupId = null, string? name = null, int? seq = null, int? minSeq = null, int? maxSeq = null, string? sort = null)
        {
            if (sort != null)
            {
                if (!"desc".Equals(sort) && !"asc".Equals(sort))
                {
                    sort = null;
                }
            }
            page = Math.Max(page.GetValueOrDefault(), 0);
            pageSize = Math.Min(Math.Max(pageSize.GetValueOrDefault(), 1), 100);

            var req = new { page, pageSize, groupId, name, seq, minSeq, maxSeq, sort };

            return GetResponseAsync<BitBrowserPageList<BitBrowserProfile>>("/browser/list", req);
        }
        /// <summary>
        /// 排列窗口以及调整窗口尺寸
        /// </summary>
        /// <param name="type">排列方式，宫格 box ， 对角线 diagonal</param>
        /// <param name="startX">起始 X 位置，默认 0</param>
        /// <param name="startY">起始 Y 位置，默认 0</param>
        /// <param name="width">宽度</param>
        /// <param name="height">高度</param>
        /// <param name="col">宫格排列时，每行列数</param>
        /// <param name="spaceX">宫格横向间距，默认 0</param>
        /// <param name="spaceY">宫格纵向间距，默认 0</param>
        /// <param name="offsetX">对角线横向偏移量</param>
        /// <param name="offsetY">对角线纵向偏移量</param>
        /// <param name="orderBy">按序号排列，asc 正序，desc 倒叙</param>
        /// <param name="ids">	要排列的窗口 ID 数组，不传则排列全部，传入时忽略 seqlist 的值</param>
        /// <param name="seqlist">要排列的窗口序号数组，不传则排列全部</param>
        /// <param name="screenId">显示器屏幕 ID，需要排列在哪个显示器上，就传入显示器 ID，具体显示器 ID，可以通过 /alldisplays 接口获取</param>
        /// <returns></returns>
        public Task<HttpResult<string>> WindowBoundsAsync(string type = "box", int? startX = 0, int? startY = 0, int? width = 500, int? height = 200, int col = 3, int? spaceX = 0, int? spaceY = 0, int? offsetX = 0, int? offsetY = 0, string? orderBy = "asc", string[]? ids = null, int[]? seqlist = null, int? screenId = null)
        {
            seqlist ??= Array.Empty<int>();
            ids ??= Array.Empty<string>();

            var req = new { type, startX, startY, width, height, col, spaceX, spaceY, offsetX, offsetY, orderBy, ids, seqlist, screenId };

            return GetResponseAsync<string>("/windowbounds", req);
        }
        /// <summary>
        /// 一键自适应排列窗口
        /// </summary>
        /// <param name="seqlist">窗口序号列表，如 [12, 14, 1889]， 不传则排列全部窗口</param>
        /// <returns></returns>
        public Task<HttpResult<string>> FlexableWindowBoundsAsync(int[]? seqlist = null)
        {
            seqlist ??= Array.Empty<int>();

            var req = new
            {
                seqlist
            };

            return GetResponseAsync<string>("/windowbounds/flexable", req);
        }
        /// <summary>
        /// 批量修改浏览器窗口分组
        /// </summary>
        /// <param name="groupId">要修改的分组编号</param>
        /// <param name="browserIds">要修改的窗口编号</param>
        /// <returns></returns>
        public Task<HttpResult<string>> UpdateBrowserGroupAsync(string groupId, string[] browserIds)
        {
            browserIds ??= Array.Empty<string>();

            var req = new
            {
                groupId,
                browserIds
            };

            return GetResponseAsync<string>("/browser/group/update", req);
        }
        /// <summary>
        /// 批量修改窗口代理信息
        /// </summary>
        /// <param name="ids">浏览器窗口 ID 数组</param>
        /// <param name="ipCheckService">IP 查询渠道，默认 ip123in，选项 ip-api, luminati，luminati 为 Luminati 专用</param>
        /// <param name="proxyMethod">代理方式，2 自定义代理，3 提取 IP，默认 2</param>
        /// <param name="proxyType">代理类型，可选http, https, socks5, ssh 默认 noproxy</param>
        /// <param name="host">代理主机</param>
        /// <param name="port">代理端口</param>
        /// <param name="proxyUserName">代理用户名</param>
        /// <param name="proxyPassword">代理密码填</param>
        /// <param name="refreshProxyUrl">代理刷新 URL 填</param>
        /// <param name="dynamicIpUrl">提取 IP url</param>
        /// <param name="dynamicIpChannel">提取 IP 服务商 rola, ipidea, deoveip, cloudam, common 默认填 common 即可</param>
        /// <param name="isDynamicIpChangeIp">true，每次打开窗口都提取新 IP， false，上次的 IP 失效时才提取新 IP</param>
        /// <param name="isIpv6">是否是 IPv6，默认 false</param>
        /// <returns></returns>
        public Task<HttpResult<string>> UpdateBrowserProxyAsync(string[] ids, string? ipCheckService = null, int? proxyMethod = null, string? proxyType = null, string? host = null, int? port = null, string? proxyUserName = null, string? proxyPassword = null, string? refreshProxyUrl = null, string? dynamicIpUrl = null, string? dynamicIpChannel = null, bool? isDynamicIpChangeIp = null, bool? isIpv6 = null)
        {
            ids ??= Array.Empty<string>();

            var req = new { ids, ipCheckService, proxyMethod, proxyType, port, proxyUserName, proxyPassword, refreshProxyUrl, dynamicIpUrl, dynamicIpChannel, isDynamicIpChangeIp, isIpv6 };

            return GetResponseAsync<string>("/browser/proxy/update", req);
        }
        /// <summary>
        /// 批量修改窗口备注
        /// </summary>
        /// <param name="browserIds">窗口 ID 数组，只改一个窗口备注时，传入一个 ID 即可</param>
        /// <param name="remark">备注信息</param>
        /// <returns></returns>
        public Task<HttpResult<string>> UpdateBrowserRemarkAsync(string[] browserIds, string? remark)
        {
            browserIds ??= Array.Empty<string>();

            var req = new { browserIds, remark };

            return GetResponseAsync<string>("/browser/remark/update", req);
        }
        /// <summary>
        /// 通过序号批量关闭窗口
        /// </summary>
        /// <param name="seqs"></param>
        /// <returns></returns>
        public Task<HttpResult<string>> CloseBrowserBySeqListAsync(int[] seqs)
        {
            seqs ??= Array.Empty<int>();
            var req = new { seqs };

            return GetResponseAsync<string>("/browser/close/byseqs", req);
        }
        /// <summary>
        /// 关闭所有的窗口
        /// </summary>
        /// <returns></returns>
        public Task<HttpResult<string>> CloseAllBrowserAsync()
        {
            return GetResponseAsync<string>("/browser/close/all", new());
        }

        /// <summary>
        /// 获取已打开窗口的进程 pid 集合，也可以用来判断窗口是否已打开，支持批量查询
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public Task<HttpResult<IDictionary<string, int>>> GetBrowserPidListAsync(string[]? ids = null)
        {
            ids??= Array.Empty<string>();

            var req = new { ids };

            return GetResponseAsync<IDictionary<string, int>>("/browser/pids", req);
        }

        /// <summary>
        /// 获取所有活着的已打开的窗口的进程 ID，会自动过滤掉已死掉的进程，无参数
        /// </summary>
        /// <returns></returns>
        public Task<HttpResult<IDictionary<string, int>>> GetAllBrowserPidListAsync()
        {
            return GetResponseAsync<IDictionary<string, int>>("/browser/pids/all", new());
        }

        /// <summary>
        /// 获取活着的给定窗口的 pids，会检查进程，减少进程退出，窗口状态没关闭的问题
        /// </summary>
        /// <param name="ids">窗口 id 集合，数组类型</param>
        /// <returns>
        /// "02d39dd4f9c54e40bc1ef51929d27235": 69902
        /// </returns>
        public Task<HttpResult<IDictionary<string, int>>> GetAliveBrowserPidListAsync(int[] ids)
        {
            ids ??= Array.Empty<int>();

            var req = new { ids };

            return GetResponseAsync<IDictionary<string, int>>("/browser/pids/alive", req);
        }

        /// <summary>
        /// 批量删除窗口，一次最多 100 个
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public Task<HttpResult<string>> DeleteBrowserByIdListAsync(string[] ids)
        {
            ids ??= Array.Empty<string>();
            if (ids.Length > 100)
            {
                ids = ids.Take(100).ToArray();
            }

            var req = new { ids };

            return GetResponseAsync<string>("/browser/delete/ids", req);
        }
        /// <summary>
        /// 清理窗口缓存，注意，会清理掉所有的本地缓存文件，和服务端缓存文件
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public Task<HttpResult<string>> ClearBrowserLocalAndServerCacheAsync(string[] ids)
        {
            ids ??= Array.Empty<string>();

            var req = new { ids };

            return GetResponseAsync<string>("/cache/clear", req);
        }
        /// <summary>
        /// 保留扩展数据，删除窗口缓存
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public Task<HttpResult<string>> ClearBrowserExceptExtensionsCacheAsync(string[] ids)
        {
            ids ??= Array.Empty<string>();

            var req = new { ids };

            return GetResponseAsync<string>("/cache/clear/exceptExtensions", req);
        }
        /// <summary>
        /// 获取所有已打开窗口的调试端口 remote-debugging-port
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public Task<HttpResult<IDictionary<string, int>>> GetBrowserRDPListAsync(string[] ids)
        {
            ids ??= Array.Empty<string>();

            var req = new { ids };

            return GetResponseAsync<IDictionary<string, int>>("/browser/ports", req);
        }
        /// <summary>
        /// 代理检测接口，可以用来查询代理信息，以及检测代理是否可用，注意如果 IP 需要在全局代理下使用，则要开全局
        /// </summary>
        /// <param name="ipCheckService">IP 检测渠道，默认 ip123in，可选 ip-api</param>
        /// <param name="host">代理主机</param>
        /// <param name="port">代理端口</param>
        /// <param name="proxyType">代理类型 http, socks5, ssh 选一</param>
        /// <param name="proxyUserName">代理用户名</param>
        /// <param name="proxyPassword">代理密码</param>
        /// <param name="checkExists">检测 IP 是否已使用，值为 1 或 0</param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserProxyCheckResult>> CheckAgentAsync(string ipCheckService = "ip123in", string? host = null, int? port = null, string? proxyType = null, string? proxyUserName = null, string? proxyPassword = null, int? checkExists = 0)
        {
            var req = new { ipCheckService, host, port, proxyType, proxyUserName, proxyPassword, checkExists };

            return GetResponseAsync<BitBrowserProxyCheckResult>("/checkagent", req);
        }
        /// <summary>
        /// 随机指纹值，传入窗口 ID，随机一次指纹，返回指纹对象，注意，不是返回窗口对象，只返回指纹对象
        /// </summary>
        /// <param name="browserId"></param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserFingerPrint>> RandomBrowserFingerprintAsync(string browserId)
        {
            var req = new { browserId };

            return GetResponseAsync<BitBrowserFingerPrint>("/browser/fingerprint/random", req);
        }
        /// <summary>
        /// 对已打开窗口设置实时
        /// </summary>
        /// <param name="browserId"></param>
        /// <param name="cookies"></param>
        /// <returns></returns>
        public Task<HttpResult<string>> UpdateOpendBrowserCookie(string browserId, BitBrowserCookie[] cookies)
        {
            cookies ??= Array.Empty<BitBrowserCookie>();

            foreach (var cookie in cookies)
            {
                cookie.ExpirationDate ??= DateTimeOffset.Now.AddYears(3).ToUnixTimeSeconds();
            }

            var req = new { browserId, cookies };

            return GetResponseAsync<string>("/browser/cookies/set", req);
        }
        /// <summary>
        /// 清空 cookie，7.0.2 及以上版本客户端支持
        /// <para>
        /// 无论窗口是否打开，此接口都能清理本地缓存中的 cookie，已同步到服务端的 cookie，用 saveSynced 字段控制，默认本地与云端一起清理掉
        /// </para>
        /// </summary>
        /// <param name="browserId"></param>
        /// <returns></returns>
        public Task<HttpResult<string>> ClearBrowserCookie(string browserId)
        {
            var req = new { browserId };

            return GetResponseAsync<string>("/browser/cookies/clear", req);
        }
        /// <summary>
        /// 获取已打开窗口的实时 cookies，注意实时 cookie 可能一直在变，两次获取到的可能不一致
        /// </summary>
        /// <param name="browserId"></param>
        /// <returns></returns>
        public Task<HttpResult<IList<BitBrowserCookie>>> GetOpendBrowserCookies(string browserId)
        {
            var req = new { browserId };

            return GetResponseAsync<IList<BitBrowserCookie>>("/browser/cookies/get", req);
        }
        /// <summary>
        /// 格式化给定 cookie，方便用户使用
        /// </summary>
        /// <param name="cookie">给定 cookie 数据，可能是数组，字符串等</param>
        /// <param name="hostname">cookie 的 domain 值，对于一些 cookie 中没有携带 domain 值的 cookie 数据，需要手动指定下 hostname 值，比如 .abc.com ，绝大部分 cookie 不需要专门指定</param>
        /// <returns></returns>
        public Task<HttpResult<BitBrowserCookie>> FormatCookie(string cookie, string hostname)
        {
            var req = new { cookie, hostname };

            return GetResponseAsync<BitBrowserCookie>("/browser/cookies/get", req);
        }
    }
}
